#ifndef FILESYSTEM_H
#define FILESYSTEM_H

/** include config.h **/
#include "config.h"

/** C++ STL Header **/
#include <iostream>
#include <string>
#include <map>
#include <cstdlib>

/** Boost Header **/
#include <boost/algorithm/string.hpp>
#include <boost/thread/mutex.hpp>
#include <boost/thread.hpp>

#include "AbstractDatabase.h"

#ifdef WINDOWS
/** Dokan Headers for Windows Port **/

#include <windows.h>
#include <dokan.h>

#else
/** FUSE Headers for Linux Port **/

#define FUSE_USE_VERSION 26 /* Set new FUSE-API */

#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include <pthread.h>
#include <fuse.h>

#endif


class Filesystem;
typedef Filesystem* PFilesystem;

typedef struct{
    String path;
	void* readBuffer;
	const void *writeBuffer;
#ifdef WINDOWS
	PFillFindData filler;
	WIN32_FIND_DATAW findData;
    DWORD size;
    LPDWORD ReadLength;
    LONGLONG offset;
    PDOKAN_FILE_INFO fi;
    LPBY_HANDLE_FILE_INFORMATION attributes;
#else
	fuse_fill_dir_t filler;
	off_t offset;
    size_t size;
    struct fuse_file_info *fi;
    struct stat *attributes;
#endif
    PFilesystem filesystem;
    double sizeParam;
    int level;
	String database;
	String schema;
	String table;
	String file;
	String extension;

    bool rwMode;
    bool readMode;
    bool writeMode;
    bool appendMode;
    bool createMode;

	bool isCache;
	bool isDirectory;
} FSInfo;

typedef FSInfo* PFSInfo;

/**
 * Memory structs and typedefs
 **/
typedef struct{
    String realPath;
    String realFileName;
    bool write;
    bool read;
    size_t size;
    size_t memSize;
    void* fileContent;
/*	String cachePath;*/
} MemoryFile;

typedef std::map<String, MemoryFile> MemoryFileMap;

typedef MemoryFile* PMemoryFile;
typedef MemoryFileMap* PMemoryFileMap;

class AbstractFileFormat;

typedef AbstractFileFormat* PFileFormat;

/**
 * file operation registry structs and typedefs
 **/

typedef struct{
    boost::thread* worker;
    bool hasWorkerThread;
	bool createMode;
	bool created;
	bool isChanged;
	bool hasWriteMainThread;
    //boost::mutex writeLock;
} FileOperationRegistry;

typedef FileOperationRegistry* PFileOperationRegistry;

typedef std::map<String, FileOperationRegistry> FileOperationRegistryMap;


typedef FileOperationRegistryMap* PFileOperationRegistryMap;


/**
 * FileHandler Struktur der die momentante Fileformat Klasse beinhaltet.
 **/
typedef struct{
    bool writeMode;
	bool deleteOnClose;
    PFileFormat file;
} FileHandler;

/**
* Ein Zeiger auf ein FileHandler Struktur.
**/
typedef FileHandler* PFileHandler;



/**
* Filesystem Klasse die alle Anfragen an die jeweilige Fileformat Klasse weiterleitet. Weiterhin ist die KLasse für das initialisieren des Dateisystems, sowie für das Auflisten der Verzeichnis- und Dateistruktur zuständig.
**/
class Filesystem{
public:
    Filesystem(PString type, PString host, PString port, PString database, PString username, PString password, PString cacheDirName, PString geomFieldName, int argc, char* argv[], size_t cacheLifeTime, bool debug);
    ~Filesystem();

    /**
    * bindet das Dateisystem ein
    **/
    int mount(int argc, char* argv[]);

    /**
    * Überprüfung ob ein Verzeichnis geöffnet werden kann.
    **/
	int openDir( PFSInfo info );

	/**
	* Listet die Verzeichnise und Dateien des Dateisystem.
	**/
    int readDir( PFSInfo info );

    /**
    * Wird beim öffnen einer Datei aufgerufen.
    **/
    int open( PFSInfo info );

    /**
    * Wird beim lesen einer Datei aufgerufen.
    **/
	int read( PFSInfo info );

	/**
	* Wird beim schreiben einer Datei aufgerufen.
	**/
	int write( PFSInfo info );

	/**
	* wird beim schließen einer Datei aufgerufen.
	**/
	int close( PFSInfo info );

    /**
    * Setzt die Dateigröße auf 0
    **/
	int truncate(PFSInfo info );

    /**
    * Setzt die Dateiinformationen für das Programm das die Information angefordert hat.
    **/
	int getattr( PFSInfo info );

    /**
    * Löscht eine Datei oder Verzeichnis. Wird vom Cache-Verzeichnis verwendet.
    **/
	int unlink( PFSInfo info );

    /**
    * Listet ein Verzeichnis auf und setzt dabei die entsprechenden Verzeichnisattribute.
    **/
	void listDir(String name, PFSInfo info);

	/**
	* Listet eine Datei auf und setzt dabei die entsprechenden Dateiattribute.
	**/
	void listFile(String name, PFSInfo info, size_t filesize);

    /**
    * Setzt Attribute an einem Verzeichnis
    **/
	void setDirAttribute(PFSInfo info, bool readOnly);

	/**
	* Setzt Dateiattribute wie z.B. Dateigröße.
	**/
    void setFileAttribute(PFSInfo info, size_t filesize);

    /**
    * Erstellt aus einen Pfad ein Array mit Allen Pfad-Elementen.
    **/
    static StringVector getPathElements(String path);

    /**
    * Extraiert den Namen einer Datei aus dem Pfad.
    **/
	static String getNameFromFile(String &filepath);

    /**
    * Gibt die momentante Datenbank Verbindung zurück.
    **/
    PDatabase getDBConnection();

    /**
    * Holt ein Datei Object aus dem Speicher(wird für das Cache-Verzeichnis benötigt).
    **/
    PMemoryFile getMemoryFile(String &file, bool isCache);

    /**
    * Legt eine neue Datei im CacheVerzeichnis an.
    **/
    void addMemoryFile(PFSInfo info);

    /**
    * Gibt eine Hash Map zurück mit allen Datei Objecten die sich im Arbeitsspeicher befinden.
    **/
    PMemoryFileMap getMemoryFileMap();

    /**
    * Gibt die Datei Registrierungs Hash Map zurück.
    **/
    PFileOperationRegistryMap getFileOperationRegistryMap();
    /*PFileOperationRegistry getFileOperationRegistry(String type, String database, String schema, String table);
	void addFileOperationRegistry(String type, String database, String schema, String table);
	void removeFileOperationRegistry(String type, String database, String schema, String table);*/

    /**
    * Gibt eine Datei operation Registereintrag zurück
    **/
	PFileOperationRegistry getFileOperationRegistry(String database, String schema, String table);

    /**
    * Registiert ein neue Datei operation.
    **/
	void addFileOperationRegistry(String database, String schema, String table);

	/**
	* Entfernt eine abgeschlossene Datei Registrierungseintrag.
	**/
	void removeFileOperationRegistry(String database, String schema, String table);

    /**
    * Extraiert aus dem PFSInfo object das Datei Handler Object.
    **/
	PFileHandler getFileHandler(PFSInfo info);

	/**
	* Setzt das Datei Handler Object, also eine Dateiformat-Klassen Object.
	**/
	void setFileHandler(PFSInfo info, PFileHandler fh);

    /**
    * Erstellt aus dem angeforderten Dateipfad ein Pfad zum Internen Cache-Verzeichnis.
    **/
	String makeCacheFileName(PFSInfo info);

    /**
    * Gibt true zurück, wenn die Dateiabfrage ignoriert werden soll. Diese Methode ist dazu da, unnötige Datenbank abfragen zu genrieren z.B. wenn der Windows Explororer nach der desktop.ini Datei sucht.
    **/
	static bool isIgnoreFile(PFSInfo info);

	/**
	* Gibt true zurück wenn die angegebe Datei unterstützt wird.
	**/
	static bool isSupportedFile(PFSInfo info, bool hasGeometry, bool hasSRID);

	/**
	* Erstellt je nach Betriebssystem aus den jeweiligen Parametern ein FSInfo object
	**/
	#ifdef WINDOWS
	static FSInfo createFSInfo(String func, LPCWSTR &path, LPVOID readBuffer, LPCVOID writeBuffer, DWORD bufferSize, LPDWORD readLength, LONGLONG offset, PFillFindData filler,LPBY_HANDLE_FILE_INFORMATION attr, PDOKAN_FILE_INFO fileInfo, PFilesystem filesystem);
	#else
	static FSInfo createFSInfo(String func, const char* path, void*readBuffer, const char* writeBuffer, fuse_fill_dir_t filler, off_t offset, size_t bufferSize, struct stat *attr, struct fuse_file_info *fileInfo, PFilesystem filesystem) ;
	#endif

    /**
    * Gibt den Standardnamen für die Geometriespalte zurück.
    **/
    String getGeomFieldName();
    /**
    * Gibt das Verzeichnis zurück wo das Dateisystem eingebunden wurde.
    * @return Einhängeverzeichnis
    **/
    String getMountDir();
    /**
    * Gibt den Pfad zum Cache-Verzeichnis zurück
    * @return Pfad zum Cache-Verzeichnis
    **/
    String getCacheDir();
    /**
    * Gibt den Namen des Cache-Verzeichnis zurück.
    * @return Name des Cache-Verzeichnis.
    **/
	String getCacheDirName();

	/**
	* Gibt den Pfad des Internen Cache-Verzeichnis zurück.
	* @return Pfad zum Cache-Verzeichnis.
	**/
	String getInternCacheDir();

	/**
	* Gibt die Lebenszeit im Cache-Verzeichnis zurück.
	* return Zeit bis die Datei gelöscht wird
	**/
	size_t getCacheLifeTime();

    /**
    * Methode um debug-Meldungen auszugeben.
    **/
	static void debug(String msg);
	//static void debug(String msg, int verboseLevel);

	/**
	* Sagt aus ob debugging aktiviert wurde.
	**/
	static bool isDebug;

	/**
	* Ein Mutex Lock damit die Debugmeldungen korrekt ausgegeben werden.
	**/
	static boost::mutex debugLock;
	int verboseLevel;

	/**
	* List von zu löschenden Dateien.
	**/
	StringVector unlinkList;
protected:
    size_t cacheLifeTime;
    String mountDir;
    String cacheDir;
	String cacheDirName;
	String internCacheDir;
	PString geometryFieldName;

    MemoryFileMap memoryFileCache;
    FileOperationRegistryMap operationRegistry;
    PDatabase dbc;

    boost::mutex writeLock;
    boost::mutex readLock;
};

/**
* Funktion zum einbinden des Dateisystem.
**/
int geodbfs_init(int argc, char* argv[], PFilesystem filesystem);

#endif
